#ifndef DEF_RingQueue
#define DEF_RingQueue

#include <iostream>
#include <vector>
#include <pthread.h>
#include "Sem.hpp"
#include "LockGuard.hpp"

#define Default_Ring_Queue 5

template <class T>
class Ring_Queue
{
public:
    Ring_Queue(size_t num = Default_Ring_Queue)
        : _v(5),
          _num(num),
          _step_in(0),
          _step_out(0),
          _value_sem(0),
          _space_sem(num)
    {
    }

    ~Ring_Queue() {}

    // 生产者
    void push(const T &in)
    {
        // 判断是否有空间可以放
        // 申请空间, 空间减少
        _space_sem.P();
        // 在多个生产者情况下，下标对于每个生产者而言是临界资源，需要互斥
        int index = _step_in;
        {
            LockGuard lock(&_Lock_P);

            ++_step_in;
            _step_in %= _num;
            _v[index] = in;
        }
        // _v[index] = in;// 这里会有bug，多线程情况下，消费者可能会先一步拿到这个下标，并对内容做修改！！！

        // _v[_step_in++] = in;
        // _step_in %= _num;
        // 放数据完成，数据增加
        _value_sem.V();
    }

    // 消费者
    void pop(T *out)
    {
        // 判断是否有数据可以拿
        // 申请数据，数据减少
        _value_sem.P();
        // 在多个消费者情况下，下标也是临界资源

        int index = _step_out;
        {

            LockGuard lock(&_Lock_C);

            ++_step_out;
            _step_out %= _num;
            *out = _v[index];
        }
        // *out = _v[index];

        // *out = _v[_step_out++];
        // _step_out %= _num;
        // 归还空间完成， 空间增加
        _space_sem.V();
    }

    size_t size()
    {
        return _step_in > _step_out ? _step_in - _step_out : _step_out - _step_in;
    }

private:
    std::vector<T> _v;
    size_t _num;
    size_t _step_in;
    size_t _step_out;
    Sem _value_sem;
    Sem _space_sem;
    Lock _Lock_C;
    Lock _Lock_P;
};

#endif